package br.senai.sc.controller.business;

import java.math.BigDecimal;

import br.senai.sc.controller.exception.BusinessException;

/**
 * Contém os cálculos de IQA para o parâmetro Sólidos.
 * 
 * @author Tiago
 */
public final class CalculoSolidosBusiness {

	/**
	 * Peso fixo utilizado para o cálculo da qualidade dos Sólidos (qi^w)
	 */
	private static final double PESO = 0.08;

	/**
	 * Define o intervalo para o cálculo dos coliformes fecais. De 0 a 150.
	 */
	public static final int[] PRIMEIRO_INTERVALO = { 0, 150 };

	/**
	 * Define o intervalo para o cálculo dos coliformes fecais. De mais que 150
	 * a 500.
	 */
	public static final int[] SEGUNDO_INTERVALO = { 150, 500 };

	/**
	 * Utilizado para a quantidade de casas decimais devem ser utilizadas para o
	 * retorno do calculo de cada intervalo de valor.
	 */
	public static final int ESCALA = 10;

	/**
	 * Utilizado para a quantidade de casas decimais devem ser utilizadas para o
	 * cálculo de exponenciais.
	 */
	private static final int ESCALA_EXP = 20;

	private CalculoSolidosBusiness() {

	}

	/**
	 * Utilizado para fazer o cálculo dos Sólidos caso o valor informado é maior
	 * ou igual a 0 e menor que 150.
	 * 
	 * Cálculo realizado: 79,75+0,166*B17-0,001088*B17^2;
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor não esteja entre o intervalo correto
	 */
	private static BigDecimal primeiroIntervalo(BigDecimal valor)
			throws BusinessException {

		// se o valor for menor que 0 ou maior que 150 é inválido
		if ((valor.compareTo(BigDecimal.ZERO) < PRIMEIRO_INTERVALO[0])
				|| (valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[1])) > 0)) {
			throw new BusinessException();
		}

		BigDecimal valorExp2 = new BigDecimal(Math.pow(valor.doubleValue(), 2))
				.setScale(ESCALA_EXP, IQABusiness.ROUND_MODE);

		// SE(B17<=150;79,75+0,166*B17-0,001088*B17^2;
		BigDecimal retorno = new BigDecimal("79.75").add(
				new BigDecimal("0.166").multiply(valor)).subtract(
				new BigDecimal("0.001088").multiply(valorExp2));

		return retorno.setScale(ESCALA, IQABusiness.ROUND_MODE);
	}

	/**
	 * Utilizado para fazer o cálculo dos Sólidos caso o valor informado é maior
	 * ou igual a 150 e menor que 500.
	 * 
	 * Cálculo realizado: 00;101,67-0,13917*B17;
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor não esteja entre o intervalo correto
	 */
	private static BigDecimal segundoIntervalo(BigDecimal valor)
			throws BusinessException {

		// se o valor informado for menor ou igual a 150 ou maior ou igual a 500
		// o mesmo está incorreto
		if ((valor.compareTo(new BigDecimal(SEGUNDO_INTERVALO[0])) <= 0)
				|| (valor.compareTo(new BigDecimal(SEGUNDO_INTERVALO[1])) > 0)) {
			throw new BusinessException();
		}

		// SE(B17<=500;101,67-0,13917*B17;
		BigDecimal retorno = new BigDecimal("101.67").subtract(new BigDecimal(
				"0.13917").multiply(valor));

		return retorno.setScale(ESCALA, IQABusiness.ROUND_MODE);
	}

	/**
	 * Calcula o IQA dos Sólidos informados. Cálculo realizado: Se [valor] < que
	 * 0 Lança exceção Se [valor] <= 150
	 * 
	 * @see {@link CalculoSolidosBusiness#primeiroIntervalo(double)} Se [valor]
	 *      <= 500
	 * @see {@link CalculoSolidosBusiness#segundoIntervalo(double)} Se [valor] >
	 *      500 IQA = 32;
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor informado seja inválido (Ex: menor que
	 *             0).
	 */
	public static BigDecimal calculaIQA(BigDecimal valor)
			throws BusinessException {
		// =SE(B17="";"NM";SE(B17<0;"ERR";
		// se o valor informado for menor que 0 o mesmo está incorreto
		if (valor.compareTo(BigDecimal.ZERO) < PRIMEIRO_INTERVALO[0]) {
			throw new BusinessException();
		}

		// SE(B17<=150;79,75+0,166*B17-0,001088*B17^2;
		if (valor.compareTo(new BigDecimal(PRIMEIRO_INTERVALO[1])) <= 0) {
			return primeiroIntervalo(valor);
		}

		// SE(B17<=500;101,67-0,13917*B17;
		if (valor.compareTo(new BigDecimal(SEGUNDO_INTERVALO[1])) <= 0) {
			return segundoIntervalo(valor);
		}

		// SE(B17>500;32)))))
		// se o valor informado é maior que 500 o retorno sempre será 32
		final int retornoMaiorQueFimUltimoIntervalo = 32;
		return new BigDecimal(retornoMaiorQueFimUltimoIntervalo);
	}

	/**
	 * Calcula a média de qualidade (qi^w).
	 * 
	 * @param valor
	 * @return
	 * @throws BusinessException
	 *             Lançada caso o valor informado seja inválido.
	 */
	protected static BigDecimal mediaQualidade(BigDecimal valor)
			throws BusinessException {
		return new BigDecimal(Math.pow(calculaIQA(valor).doubleValue(), PESO));
	}

}
